//将对象转换成查询字符串的方法
function queryStringify(obj){
    let str = ''
    for(let k in obj){
        str += k + '=' + obj[k] + '&'
    }
    return str.slice(0,-1)
}
//封装 ajax
//目的：为了发送ajax请求，向服务器获取数据
//使用：给开发人员使用
function ajax(options = {}){
//object 是一个对象，传递的是本次请求所有相关的信息
// 给object 设置一个参数默认值， 表示当你没有传options 的时候, 使用空对象
// 目的: 后期访问 options 内的内容的时候, 可以不报错

// 0、从 options 内拿到所有的信息, 为了后面验证的时候使用方便
//解构函数
const {url,method,async,data,headers,dataType,success} = options

//1、验证options 内的信息
 //验证 url 信息 （请求地址）
 // 此项为必填项
 if(!url){
    //表示 没有url 这一项
    //手动制造一个错误 并抛到运行程序 起到中断程序作用
    throw new Error('url 为必填项，请传递此项内容')
 } 
 //验证 method 信息（请求方式）
 //  可以不写 只要写了必须是'GET' 或者'POST'
 // 判断可以是 undefined, 也可以是 get 或者 post, 其他的都不行
 if(!(method === undefined || /^(get|post)$/i.test(method))){
     //表示： method 既不是 undefined 也不是 get 或者post
     //手动制造一个错误 并抛到运行程序 起到中断程序作用
    throw new Error('method 目前只接收"GET"或者"POST"方式')
 }
 //验证 async 信息（是否异步）
 //可以不写  只要写了 就必须是true 或者 是false
 if(!(async === undefined || typeof async === 'boolean')){
    //表示：  async 既不是 undefiend 也不是 布尔值
     //手动制造一个错误 并抛到运行程序 起到中断程序作用
     throw new Error('async 需要传递一个 "Boolean" 类型数据')
 }
 //验证 data 信息（带给后端的数据）
  // 可以不写, 但是你要是写了, 得是 字符串类型, 也可以是对象数据类型
 if(!(data === undefined || typeof data === 'string' || Object.prototype.toString.call(data) === '[object Object]')){
     //表示 ： data 既不是 undefiend 也不是字符串, 也不是 对象数据类型
     //手动制造一个错误 并抛到运行程序 起到中断程序作用
     throw new Error('data 需要传递一个 "String" 或者 "Object" 类型数据')
 }
 //验证 headers 信息（请求头信息）
 // 可以不写, 但是你要是写了, 得是 对象数据类型
 if(!(headers === undefined || Object.prototype.toString.call(headers) === '[object Object]')){
     //表示 ： headers 既不是 undefiend 也不是 对象数据类型
     //手动制造一个错误 并抛到运行程序 起到中断程序作用
     throw new Error('headers 需要传递一个 "Object" 类型数据')
 }
 //验证 dataType 信息（是否解析响应）
 // 可以不写, 但是你要是写了, 必须是字符串 'string' 或者 'json'
 if(!(dataType === undefined || /^(string|json)$/i.test(dataType))){
     //表示 ： dataType 既不是 undefiend 也不是 字符串 'string' 或者 'json'
     //手动制造一个错误 并抛到运行程序 起到中断程序作用
     throw new Error('dataType 目前只接受 "json" 或者 "string" 两种值')
 }
 //验证 success 信息（成功以后执行的函数）
 // 可以不写, 但是你要是写了, 必须是函数数据类型
 if(!(success === undefined || typeof success === 'function')){
     //表示 ： success 既不是 undefiend 也不是 函数
     //手动制造一个错误 并抛到运行程序 起到中断程序作用
     throw new Error('success 需要传递一个 "Function" 类型数据')
 }
//  准备默认值
const _default = {
    //代码执行到这 url 肯定有内容
    url:url,
    // 代码来到这里, method 要么是 undefined, 要么是 get 要么是 post
    method:method || 'GET',
    // 代码来到这里, async 要么是 undefined, 要么是 true 要么是 false
    // //方案一
    // async:typeof async === 'boolean' ? async : true,
    //方案二：空值运算(ES2020) ??
    // 语法：值 ?? 值, 只有左边为 空 的时候, 才会使用右边
    async:async ?? true,
    // 代码来到这里, data 要么是 undefined, 要么是 字符串, 要么是 对象
    data:data || '',
    // 代码来到这里, headers 要么是 undefined, 要么是 对象数据类型的时候
    //在默认值的基础上添加新属性  使用 （展开运算符 ...）
    headers:{'content-type':'application/x-www-form-urlencoded',...headers},
    // 代码来到这里, dataType 要么是 undefined, 要么是 'json' 要么是 'string'
    dataType:dataType || 'string',
     // 代码来到这里, success 要么是 undefined, 要么是 函数
     success:success || function(){}
}
//调整data 的值
// 当 data 为 对象数据类型的时候, 需要转换成 查询字符串
// 因为代码执行到这里, _default 只有两种可能了, 字符串 或者 对象
if(typeof _default.data === 'object'){
    // 表示 data 是一个对象数据类型
    // 把 _default.data 数据转化成 查询字符串
    _default.data = queryStringify(_default.data)
}
//调整一下 url 的值
  // 如果你是 get 请求, 并且 _default.data 有内容, 那么我就直接拼接好
  // 代码执行到这里, _default.data, 要么是 '', 要么是有内容得字符串
if(/^get$/i.test(_default.method) && _default.data){
      // 这个 if 条件可以执行, 说明 method 是 get, data 有数据
     _default.url += '?' + _default.data
  }

//发送请求
//创建一个ajax 对象
const xhr = new XMLHttpRequest()
//配置本次请求信息
xhr.open(_default.method,_default.url,_default.async)
//获取后端给的信息（依赖事件触发）
xhr.onload = function(){
    //判断打印结果是否用解析
    const res = _default.dataType === 'string' ? xhr.responseText : JSON.parse(xhr.responseText)
    //调用_default.success 函数
    _default.success(res)
}
// 只要 headers 内有内容, 就设置到请求头上
// _default.headers 内有多少个信息, 设置多少条请求头
for(let k in _default.headers){
    // k 就是每一个名字,
    // _default.headers[k] 就是每个值
    xhr.setRequestHeader(k, _default.headers[k])
}
// 分辨一下, 是 get 还是 post 请求
//来确定 发送请求是否携带参数
/^get$/i.test(_default.method) ? xhr.send() : xhr.send(_default.data)
}
// 使用 Promise 语法 对ajax 进行二次封装
function pAjax(options ={}){
    // 异步的 ajax 代码
   // 利用 Promise 语法
   return new Promise((resolve,reject)=>{
    //书写 ajax 代码
    ajax({
      url: options.url,
      data: options.data,
      async: options.async,
      method: options.method,
      headers: options.headers,
      dataType: options.dataType,
      success (res) {
        // 调用 resolve 让本次 Promise 转换为成功
        resolve(res)
      }
  })
     })
 }